package com.gitee.wsl.struct.generator.sample

import com.gitee.wsl.ext.base.currentTimeMillis
import com.gitee.wsl.ext.string.toBinaryString
import com.gitee.wsl.time.toEpochMilliseconds
import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlinx.datetime.LocalDateTime
import kotlin.jvm.JvmOverloads


/**
 * 16位数字卡号生成器
 * 卡号固定为16位，53bit，格式如下（各字段位数可通过全参构造函数调整）：
 * +=======================================================================
 * | 3bit卡类型 | 31bit时间戳 | 3bit机器编号  | 9bit序号 | 7bit卡号校验位 |
 * +=======================================================================
 *
 *
 * 3 bit 卡类型，支持8种卡类型。
 * 31 bit 的秒时间戳支持68年
 * 9 bit 序号支持512个序号
 * 3 bit 机器编号支持8台负载
 *
 *
 * 即卡号生成最大支持8台负载，每台负载每秒钟可以生成512个卡号。
 * 时间戳、机器编号、序号和校验位的bit位数支持业务自定义，方便业务定制自己的生成器。
 *
 * @author liuzhaoming
 * @date 2017/11/22
 */
class CardIdGenerator : CardIdGeneratorable, SynchronizedObject {
    /**
     * 时间bit数，时间的单位为秒，31 bit位时间可以表示68年
     */
    private var timeBits = 31

    /**
     * 机器编码bit数
     */
    private var machineBits = 3

    /**
     * 每秒序列bit数
     */
    private var sequenceBits = 9

    /**
     * 校验bit位数
     */
    private var validationBits = 7

    /**
     * 上一次时间戳
     */
    private var lastStamp = -1L

    /**
     * 系统编号,默认为1
     */
    private var defaultSystem = 1L

    /**
     * 系统编号左移bit数
     */
    private var systemOffset = 0

    /**
     * 序列
     */
    private var sequence = 0L

    /**
     * 机器编号
     */
    private var machineId = 1L

    /**
     * 时间左移bit数
     */
    private var timeOffset = 0

    /**
     * 机器编码左移bit数
     */
    private var machineOffset = 0

    /**
     * 序列左移bit数
     */
    private var sequenceOffset = 0

    /**
     * 最大序列号
     */
    private var maxSequence = 0L

    /**
     * 最大校验码
     */
    private var maxCode = 0

    /**
     * 开始时间，默认为2019-01-01
     */
    private var startTimeString = "2019-01-01 00:00:00"

    /**
     * 起始时间戳
     */
    private var startTimeStamp = 0L

    @JvmOverloads
    constructor(machineId: Int = 1, defaultSystem: Int = 1) {
        if (defaultSystem < 1 || defaultSystem > 7) {
            throw IllegalArgumentException("The default system must be in [1, 7]")
        }

        val maxMachineId = (-1 shl machineBits).inv()
        if (machineId > maxMachineId) {
            throw IllegalArgumentException(
                "Machine bits is " + machineBits + ", so the max machine id is " + maxMachineId
            )
        }

        this.machineId = machineId.toLong()
        this.defaultSystem = defaultSystem.toLong()
        init()
    }

    /**
     * 全参构造函数，便于业务定制卡号生成器
     *
     * @param timeBits        时间bit数
     * @param machineBits     机器编码bit数
     * @param sequenceBits    每秒序列bit数
     * @param validationBits  校验bit位数
     * @param machineId       机器编号
     * @param startTimeString 开始时间
     * @param defaultSystem   默认系统编号
     */
    constructor(
        timeBits: Int, machineBits: Int, sequenceBits: Int, validationBits: Int, machineId: Int,
        startTimeString: String?, defaultSystem: Int,
    ) {
        if ((timeBits <= 0) || (machineBits <= 0) || (sequenceBits <= 0) || (validationBits <= 0)) {
            throw IllegalArgumentException("The bits should be larger than 0")
        }
        if (timeBits + machineBits + sequenceBits + validationBits != 50) {
            throw IllegalArgumentException(
                "The sum of timeBits and machineBits and sequenceBits " +
                        "and validationBits should be 50"
            )
        }

        val maxMachineId = (-1 shl machineBits).inv()
        if (machineId > maxMachineId) {
            throw IllegalArgumentException(
                ("Machine bits is " + machineBits
                        + ", so the max machine id is " + maxMachineId)
            )
        }

        if (defaultSystem < 1 || defaultSystem > 7) {
            throw IllegalArgumentException("The default system must be in [1, 7]")
        }

        this.defaultSystem = defaultSystem.toLong()
        this.timeBits = timeBits
        this.machineBits = machineBits
        this.sequenceBits = sequenceBits
        this.validationBits = validationBits
        this.machineId = machineId.toLong()
        if (null != startTimeString) {
            this.startTimeString = startTimeString
        }
        init()
    }

    /**
     * 生成16位卡号
     *
     * @return 16位卡号
     */
    fun generate(): Long {
        return generate(defaultSystem)
    }

    /**
     * 根据给定的系统编号生成卡号
     *
     * @param system 系统编号
     * @return 16位卡号
     */

    fun generate(system: Long): Long = synchronized(this){
        if (system < 1 || system > 7) {
            throw IllegalArgumentException("The system must be in [1, 7]")
        }

        var curStamp = currentSecond
        if (curStamp < lastStamp) {
            throw IllegalArgumentException("Clock moved backwards. Refusing to generate id.")
        }

        if (curStamp == lastStamp) {
            sequence = (sequence + 1) and maxSequence
            if (sequence == 0L) {
                curStamp = nextSecond
            }
        } else {
            sequence = 0L
        }
        lastStamp = curStamp
        val originId = (system shl systemOffset
                ) or ((curStamp - startTimeStamp) shl timeOffset
                ) or (machineId shl machineOffset
                ) or (sequence shl sequenceOffset)

        val validationCode: Int = CardIdGeneratorable.getValidationCode(originId, maxCode)
        return originId + validationCode
    }

    /**
     * 校验卡号是否合法
     *
     * @param id 卡号
     * @return boolean 合法返回true，反之false
     */
    fun validate(id: Long): Boolean {
        if (id > MAX_ID || id < MIN_ID) {
            return false
        }

        return validateCode(id, startTimeStamp, timeBits, timeOffset, validationBits, maxCode)
    }

    /**
     * 解析卡号
     *
     * @param id 卡号
     * @return 解析结果依次是系统编号（system）、时间戳、机器编码、序列号
     */
    fun parse(id: Long): Array<Long>? {
        if (!validate(id)) {
            return null
        }
        val bitString: String = id.toString().toBinaryString()
        val bitLength = bitString.length
        val system = bitString.substring(0, bitLength - systemOffset).toLong(2)
        val timestamp =
            bitString.substring(bitLength - timeOffset - timeBits, bitLength - timeOffset).toLong(2)
        val machineId = bitString.substring(
            bitLength - machineOffset - machineBits,
            bitLength - machineOffset
        ).toLong(2)
        val sequence = bitString.substring(
            bitLength - sequenceOffset - sequenceBits,
            bitLength - sequenceOffset
        ).toLong(2)
        return arrayOf(system, (timestamp + startTimeStamp) * 1000, machineId, sequence)
    }

    /**
     * 数据初始化
     */
    private fun init() {
        sequenceOffset = validationBits
        machineOffset = sequenceOffset + sequenceBits
        timeOffset = machineOffset + machineBits
        systemOffset = timeOffset + timeBits
        maxSequence = (-1L shl sequenceBits).inv()
        startTimeStamp = LocalDateTime.parse(startTimeString).toEpochMilliseconds()
        maxCode = (-1 shl validationBits).inv()
    }

    private val currentSecond: Long
        /**
         * 获取当前时间戳 单位秒
         *
         * @return 时间戳（秒）
         */
        get() = currentTimeMillis / 1000

    private val nextSecond: Long
        /**
         * 获取下一秒钟
         *
         * @return 时间戳（秒）
         */
        get() {
            var second = currentSecond
            while (second <= lastStamp) {
                //sleep(20)
                second = currentSecond
            }
            return second
        }

    companion object {
        /**
         * 最大ID
         */
        private val MAX_ID = 9999999999999999L

        /**
         * 最小ID
         */
        private val MIN_ID = 1000000000000000L
    }
}
